﻿using System;
using System.IO;
using Tao.OpenGl;

namespace Aeon.Framework
{

/// <summary>
/// Dynamic mesh heightmap terrain object class.
/// 
/// </summary>
public class Groundmap : IObject    // [BUG] Memory leak. Somewhere...
    {
    // --- Attributes:

    private AeonColor color;        // Color tone.
    private ITexture texture;       // Texture image.
    
    private UInt16 sizeMap;         // Raw file data size.
    private UInt16 sizeStep;        // Quad size precision.

    private Byte[] map;             // Heightmap file data.
    //private Byte[,] matrix;         // Vertex coordinates matrix. // [TODO] Generate and postprocess vertex map once.

    // --- Constructor:

    public Groundmap(String key, Position center, Orientation face, String path, AeonColor color, ITexture texture)
        : base(key,center,face)
        {
        this.texture = texture;
        this.color = color;
        sizeStep = 8;
        
        map = loadMap(path);
        //matrix = loadMatrix();
        
        //smoothMatrix();
        }

    // --- Internal Methods:

    /// <summary>
    /// Load file to map.
    /// 
    /// </summary>
    private byte[] loadMap(String path)
        {
        FileStream fs = new FileStream(path,FileMode.Open,FileAccess.Read);
            
        Int64 length = fs.Seek(0,SeekOrigin.End);
        fs.Seek(0, SeekOrigin.Begin);
        
        Byte[] data = new Byte[length];
        sizeMap = Convert.ToUInt16(Math.Sqrt(length));
        
        BinaryReader br = new BinaryReader(fs);
        data = br.ReadBytes(sizeMap*sizeMap);
        
        br.Close();
        fs.Close();
            
        return(data);
        }

    /// <summary>
    /// Get height of the vertex.
    /// 
    /// </summary>
    private byte getHeight(int x,int z)
        {
        x = x % sizeMap;
        z = z % sizeMap;

        return (map[x+(z*sizeMap)]);
        }        

    /// <summary>
    /// Load map to vertex matrix.
    /// 
    /// </summary>
    //private byte[,] loadMatrix()
    //    {
    //    int limit = sizeMap-sizeStep-1;
    //    byte[,] matrix = new byte[limit,limit];
        
    //    for (ushort X=0; X<limit; X+=sizeStep)
    //        {
    //        for (ushort Z=0; Z<limit; Z+=sizeStep)
    //            {
    //            matrix[X,Z] = getHeight(X,Z);
    //            matrix[X,Z+sizeStep] = getHeight(X,Z+sizeStep);
    //            matrix[X+sizeStep,Z+sizeStep] = getHeight(X+sizeStep,Z+sizeStep);
    //            matrix[X+sizeStep,Z] = getHeight(X+sizeStep,Z);
    //            }
    //        }

    //    return(matrix);
    //    }
        
    /// <summary>
    /// Averages adjacent vertexes for a smoother terrain.
    /// 
    /// </summary>
    //private void smoothMatrix()
    //    {
    //    for (ushort X = 1; X < sizeMap; X ++)
    //        {
    //        for (ushort Z = 1; Z < sizeMap * sizeStep/2; Z++)
    //            {
    //            int Y = matrix[X,Z];
                
    //            Y += matrix[X,Z-1];    Y += matrix[X,Z+1];
    //            Y += matrix[X-1,Z-1];  Y += matrix[X-1,Z+1];  Y += matrix[X-1,Z];
    //            Y += matrix[X+1,Z-1];  Y += matrix[X+1,Z+1];  Y += matrix[X+1,Z];
    //            Y = Y/9;
                
    //            matrix[X,Z] = (byte)Y ;
    //            }
    //        }
    //    }

    /// <summary>
    /// Set the hue bump value for the vertex.
    /// 
    /// </summary>
    private void setHue(int x, int y)
        {        
        float hue = -0.15f + (getHeight(x,y)/256.0f);
        
        Gl.glColor4f(color.R*hue,color.G*hue,color.B*hue,color.A);
        }
        
    // --- External Methods:

    public override void Draw() // [TODO] Custom size. Strechted textures. Clean code.
        {
        Gl.glTranslated(origin.xPos-sizeMap/2,origin.yPos,origin.zPos-sizeMap/2);   // [BUG] Rotation is translating.
        Gl.glRotated(face.xAng,1,0,0);
        Gl.glRotated(face.yAng,0,1,0);
        Gl.glRotated(face.zAng,0,0,1);
        
        texture.Bind();

        Gl.glEnable(Gl.GL_TEXTURE_GEN_S);
        Gl.glEnable(Gl.GL_TEXTURE_GEN_T);
        Gl.glTexGeni(Gl.GL_S,Gl.GL_TEXTURE_GEN_MODE,Gl.GL_OBJECT_LINEAR);
        Gl.glTexGeni(Gl.GL_T,Gl.GL_TEXTURE_GEN_MODE,Gl.GL_OBJECT_LINEAR);

        //Gl.glTexParameteri(Gl.GL_TEXTURE_2D,Gl.GL_TEXTURE_WRAP_S,Gl.GL_CLAMP);
        //Gl.glTexParameteri(Gl.GL_TEXTURE_2D,Gl.GL_TEXTURE_WRAP_T,Gl.GL_CLAMP);
        //Gl.glTexParameteri(Gl.GL_TEXTURE_2D,Gl.GL_TEXTURE_WRAP_S,Gl.GL_REPEAT);
        //Gl.glTexParameteri(Gl.GL_TEXTURE_2D,Gl.GL_TEXTURE_WRAP_T,Gl.GL_REPEAT);
        
        //Gl.glPushMatrix();
        Gl.glBegin(Gl.GL_QUADS);        // Polygons.
        //Gl.glBegin(Gl.GL_LINES);      // Wireframe.
        
        int max = sizeMap-sizeStep;
        for (ushort X=0; X<max; X+=sizeStep)
            {
            for (ushort Z=0; Z<max; Z+=sizeStep)
                {
                // Bottom Left Vertex
                int x = X;
                int y = getHeight(X,Z);
                int z = Z;

                //Gl.glTexCoord2f(0,0);
                setHue(x, z);
                Gl.glVertex3i(x, y, z);

                // Top Left Vertex
                x = X;
                y = getHeight(X, Z + sizeStep);
                z = Z + sizeStep;

                //Gl.glTexCoord2f(0,1);
                setHue(x, z);                
                Gl.glVertex3i(x, y, z);

                // Top Right Vertex
                x = X + sizeStep;
                y = getHeight(X + sizeStep, Z + sizeStep);
                z = Z + sizeStep;

                //Gl.glTexCoord2f(1,0);
                setHue(x, z);
                Gl.glVertex3i(x, y, z);

                // Bottom Right Vertex
                x = X + sizeStep;
                y = getHeight(X + sizeStep, Z);
                z = Z;

                //Gl.glTexCoord2f(1,1);
                setHue(x, z);
                Gl.glVertex3i(x, y, z);
                }
            }
            
        Gl.glEnd();
        //Gl.glPopMatrix();

        Gl.glDisable(Gl.GL_TEXTURE_GEN_S);
        Gl.glDisable(Gl.GL_TEXTURE_GEN_T);
        }

    }


}